#ifndef MODEL_MAN_HPP_INCLUDED
#define MODEL_MAN_HPP_INCLUDED

/**************************************************************************************************
 *
 * This file contains a model of human being.
 *
 **************************************************************************************************/

#include <set>
#include <map>
#include <memory>
#include <algorithm>
#include <typeinfo>
#include "rapidxml/rapidxml.hpp"
#include "rapidxml/rapidxml_utils.hpp"
#include "rapidxml/rapidxml_print.hpp"
#include "tools/basic.hpp"

#define EN_Sumulate                       1u
#define EN_LogMsg                         1u // set EN_LogMsg to 1 log message, set 0 otherwise.

#define LEN_Tag                           20 // max length of string of Tag.


#if EN_LogMsg > 0u
  #define  __logMsg(...) printf(__VA_ARGS__)
#else
  #define  __logMsg(...) (void *)0
#endif

namespace shooter
{
namespace model_man
{

  /**
   * @brief  Enum of content of model.
   */
  enum Model_Content_e
  {
    MODEL_Finger = 0,
    MODEL_Hand,
    MODEL_Head,
    MODEL_NUM,
  };

  /**
   * @brief  Use a template struct for static const value beacuse hpp file can not have static
   *         value unless using template.
   */
  template <int dummy>
  struct parms
  {
    static const char C_strType_Finger[LEN_Tag];
    static const char C_strType_Plam[LEN_Tag];

    static const char C_strTag_Model[LEN_Tag];
    static const char C_strTag_Name[LEN_Tag];
    static const char C_strTag_Type[LEN_Tag];
    static const char C_strTag_Rho[LEN_Tag];
    static const char C_strTag_Alpha[LEN_Tag];
    static const char C_strTag_Beta[LEN_Tag];
    static const char C_strTag_Root[LEN_Tag];
  };

  template <int dummy>
  const char parms<dummy>::C_strType_Finger[LEN_Tag] = "finger";
  template <int dummy>
  const char parms<dummy>::C_strType_Plam[LEN_Tag] = "plam";

  template <int dummy>
  const char parms<dummy>::C_strTag_Model[LEN_Tag] = "model";
  template <int dummy>
  const char parms<dummy>::C_strTag_Name[LEN_Tag] = "name";
  template <int dummy>
  const char parms<dummy>::C_strTag_Type[LEN_Tag] = "type";
  template <int dummy>
  const char parms<dummy>::C_strTag_Rho[LEN_Tag] = "rho";
  template <int dummy>
  const char parms<dummy>::C_strTag_Alpha[LEN_Tag] = "alpha";
  template <int dummy>
  const char parms<dummy>::C_strTag_Beta[LEN_Tag] = "beta";
  template <int dummy>
  const char parms<dummy>::C_strTag_Root[LEN_Tag] = "root";

  /**
   * @brief  Class to represent a conponent of human being e.g. hand.
   *
   * @note   1. It has a base point that represent the whole conponent, and a root coponent to
   *            represent which conponent it belongs to (such as finger's root is hand).
   *         2. A base class.
   */
  template <class T = double>
  class conponent_t
  {
  public:
    conponent_t(): conponent_root_(0){}
    virtual ~conponent_t(){}

    basic::point_t<T> & point_base(){ return point_base_; }
    conponent_t *& conponent_root(){ return conponent_root_; }

  private:
    /** @note No copy and assignment! */
    conponent_t(const conponent_t & copy){}
    conponent_t operator=(const conponent_t & copy){}

    basic::point_t<T> point_base_; // the base point of this.
    conponent_t * conponent_root_; // the pointer of conponent that is the root of this object.
  };


  /**
   * @brief  Class to represent a human being.
   *
   * @note   1. Use a map from conponent name to conponent to store conponnet.
   *         2. Use a multi map from conponent group to conponent to classify conponent.
   */
  template <class T = double>
  class model_man_t
  {
  public:
    int load_modelParms(const std::string & xml_name)
    {
      int ret = basic::ERR_None;
      char* ch = NULL;

      try // rapidxml might throw out the exception.
      {
        rapidxml::file<> xml_file(xml_name.c_str());
        rapidxml::xml_document<> xml_doc;
        xml_doc.parse<0>(xml_file.data());

        rapidxml::xml_node<>* root_node = xml_doc.first_node();
        ret = basic::ERR_XML_WrongNode;
        if(root_node != NULL)
        {
          rapidxml::xml_node<>* finger_node = root_node->first_node(parms<0>::C_strTag_Model);
          for(; finger_node != NULL; finger_node=finger_node->next_sibling(parms<0>::C_strTag_Model))
          {
            ret = basic::ERR_XML_WrongAttr;
            rapidxml::xml_attribute<>* attr_name = finger_node->first_attribute(parms<0>::C_strTag_Name);
            rapidxml::xml_attribute<>* attr_type = finger_node->first_attribute(parms<0>::C_strTag_Type);
            rapidxml::xml_attribute<>* attr_rho = finger_node->first_attribute(parms<0>::C_strTag_Rho);
            rapidxml::xml_attribute<>* attr_alpha = finger_node->first_attribute(parms<0>::C_strTag_Alpha);
            rapidxml::xml_attribute<>* attr_beta = finger_node->first_attribute(parms<0>::C_strTag_Beta);
            rapidxml::xml_attribute<>* attr_root = finger_node->first_attribute(parms<0>::C_strTag_Root);
            __logMsg("name:%s type:%s rho:%s alpha:%s beta:%s root:%s\r\n",
                     attr_name->value(), attr_type->value(), attr_rho->value(), attr_alpha->value(), attr_beta->value(), attr_root->value());
            if((attr_name == NULL) || (attr_type == NULL) || (attr_rho == NULL)
               || (attr_alpha == NULL)  || (attr_beta == NULL) || (attr_root == NULL))
            {
              break;
            }
            /* Set the coordinate of conponent relative to its root conponent. */
            double rho = atof(attr_rho->value());
            double alpha = atof(attr_alpha->value());
            double beta = atof(attr_beta->value());
            std::shared_ptr<conponent_t<T>> ptr_conp(new conponent_t<T>());
            ptr_conp->point_base().set_coor(rho, alpha, beta, 0);
            __logMsg("coor_offset: %f %f %f\r\n",
                     ptr_conp->point_base().get_coor().polor().rho_, ptr_conp->point_base().get_coor().polor().alpha_, ptr_conp->point_base().get_coor().polor().beta_);
            __logMsg("coor_offset: %f %f %f\r\n",
                     ptr_conp->point_base().get_coor().rect().x_, ptr_conp->point_base().get_coor().rect().y_, ptr_conp->point_base().get_coor().rect().z_);

            /* Find the root conponent from map, link it. */
            auto iter = map_nameToConp_.find(attr_root->value());
            if(iter != map_nameToConp_.end())
            {
              ptr_conp->conponent_root() = NULL;//&(*(iter->second));
              __logMsg("root conponent is: %s\r\n", attr_root->value());
            }

            /* Put the conponent into map_nameToConp_ and put the name into mmap_typeToName_. */
            std::string str_name = attr_name->value();
            std::string str_type = attr_type->value();
            map_nameToConp_.insert(std::make_pair(str_name, ptr_conp));
            mmap_typeToName_.insert(std::make_pair(str_type, str_name));
            __logMsg("ref count:%d\r\n", (int)ptr_conp.use_count());
          }
        }
      }
      catch(std::string & msg)
      {
        __logMsg("can not open file: %s.\r\n", msg.c_str());
        ret = basic::ERR_XML_WrongFile;
      }
      catch(...)
      {
        __logMsg("can not open file.\r\n");
        ret = basic::ERR_XML_WrongFile;
      }
      return ret;
    }

  private:
    /** @note because \code'conponent_t' can not copy or assign, so we use a shared_ptr to store the value. */
    std::map<std::string, std::shared_ptr<conponent_t<T>>> map_nameToConp_;
    std::multimap<std::string, std::string> mmap_typeToName_;
  };

} // namespace model_man
} // namespace shooter



#endif
